/**
  ******************************************************************************
  * @file    py32f410_hal_opa.c
  * @author  MCU Application Team
  * @brief   OPA HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the operational amplifiers peripheral:
  *           + Initialization/de-initialization functions
  *           + I/O operation functions
  *           + Peripheral Control functions
  *           + Peripheral State functions
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 Puya Semiconductor Co.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by Puya under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "py32f4xx_hal.h"

/** @addtogroup PY32F410_HAL_Driver
  * @{
  */

#ifdef HAL_OPA_MODULE_ENABLED

/** @defgroup OPA OPA
  * @brief OPA HAL module driver
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/** @defgroup OPA_Private_Define OPA Private Define
  * @{
  */
/* CSR register reset value */
#define OPA_CSR_RESET_VALUE              (0x00000000UL)

#define OPA_OUTPUT_LEVEL_BITOFFSET_POS   (OPA_CSR_OPA_CMP_OUT_Pos)

/**
  * @}
  */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Exported functions ---------------------------------------------------------*/

/** @defgroup OPA_Exported_Functions OPA Exported Functions
  * @{
  */

/** @defgroup OPA_Exported_Functions_Group1 Initialization and de-initialization functions
  *  @brief    Initialization and Configuration functions
  *
@verbatim
 ===============================================================================
              ##### Initialization and de-initialization  functions #####
 ===============================================================================
    [..]  This section provides functions allowing to:

@endverbatim
  * @{
  */

/**
  * @brief  Initializes the OPA according to the specified
  *         parameters in the OPA_InitTypeDef and initialize the associated handle.
  * @note   If the selected opa is locked, initialization can't be performed.
  *         To unlock the configuration, perform a system reset.
  * @param  hopa OPA handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPA_Init(OPA_HandleTypeDef *hopa)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPA handle allocation and lock status */
  /* Init not allowed if calibration is ongoing */
  if (hopa == NULL)
  {
    return HAL_ERROR;
  }
  else if (hopa->State == HAL_OPA_STATE_BUSYLOCKED)
  {
    return HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPA_ALL_INSTANCE(hopa->Instance));

#if (USE_HAL_OPA_REGISTER_CALLBACKS == 1)
    if (hopa->State == HAL_OPA_STATE_RESET)
    {
      if (hopa->MspInitCallback == NULL)
      {
        hopa->MspInitCallback               = HAL_OPA_MspInit;
      }
    }
#endif /* USE_HAL_OPA_REGISTER_CALLBACKS */

    /* Init SYSCFG and the low level hardware to access opa */
    __HAL_RCC_SYSCFG_CLK_ENABLE();

    if (hopa->State == HAL_OPA_STATE_RESET)
    {
      /* Allocate lock resource and initialize it */
      hopa->Lock = HAL_UNLOCKED;
    }

#if (USE_HAL_OPA_REGISTER_CALLBACKS == 1)
    hopa->MspInitCallback(hopa);
#else
    /* Call MSP init function */
    HAL_OPA_MspInit(hopa);
#endif /* USE_HAL_OPA_REGISTER_CALLBACKS */

    MODIFY_REG(hopa->Instance->CSR,OPA_CSR_OPAINTOEN, hopa->Init.OutputSelect);

    if(hopa->Instance == OPA2)
    {
      MODIFY_REG(hopa->Instance->CSR,OPA_CSR_OPA_CMP_CR, hopa->Init.Mode);

       /* Set digital filter */
      if (hopa->Init.DigitalFilter == 0)
      {
        /* Disable digital filter */
        CLEAR_BIT(hopa->Instance->FR, OPA_FR_FLTEN);
      }
      else
      {
        WRITE_REG(hopa->Instance->FR, (OPA_FR_FLTEN | ((hopa->Init.DigitalFilter) << OPA_FR_FLTCNT_Pos)));
      }
      
      if(hopa->Init.ITMode == ENABLE)
      {
        SET_BIT(hopa->Instance->INTR, OPA_INTR_INTEN);
      }
      else
      {
        CLEAR_BIT(hopa->Instance->INTR, OPA_INTR_INTEN);
      }
    }
 
    /* Update the OPA state*/
    if (hopa->State == HAL_OPA_STATE_RESET)
    {
      /* From RESET state to READY State */
      hopa->State = HAL_OPA_STATE_READY;
    }
    /* else: remain in READY or BUSY state (no update) */

    return status;
  }
}


/**
  * @brief  DeInitializes the OPA peripheral
  * @note   Deinitialization can't be performed if the OPA configuration is locked.
  *         To unlock the configuration, perform a system reset.
  * @param  hopa OPA handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPA_DeInit(OPA_HandleTypeDef *hopa)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPA handle allocation */
  /* DeInit not allowed if calibration is ongoing */
  if (hopa == NULL)
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPA_ALL_INSTANCE(hopa->Instance));

    /* Set OPA_CSR register to reset value */
    WRITE_REG(hopa->Instance->CSR, OPA_CSR_RESET_VALUE);

    /* DeInit the low level hardware: GPIO, CLOCK and NVIC */
    /* When OPA is locked, unlocking can be achieved thanks to */
    /* __HAL_RCC_SYSCFG_CLK_DISABLE() call within HAL_OPA_MspDeInit */
    /* Note that __HAL_RCC_SYSCFG_CLK_DISABLE() also disables comparator */

#if (USE_HAL_OPA_REGISTER_CALLBACKS == 1)
    if (hopa->MspDeInitCallback == NULL)
    {
      hopa->MspDeInitCallback = HAL_OPA_MspDeInit;
    }
    /* DeInit the low level hardware */
    hopa->MspDeInitCallback(hopa);
#else
    HAL_OPA_MspDeInit(hopa);
#endif /* USE_HAL_OPA_REGISTER_CALLBACKS */

    hopa->State = HAL_OPA_STATE_RESET;

    /* Process unlocked */
    __HAL_UNLOCK(hopa);
  }

  return status;
}

/**
  * @brief  Initialize the OPA MSP.
  * @param  hopa OPA handle
  * @retval None
  */
__weak void HAL_OPA_MspInit(OPA_HandleTypeDef *hopa)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hopa);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_OPA_MspInit could be implemented in the user file
   */

  /* Example */
}

/**
  * @brief  DeInitialize OPA MSP.
  * @param  hopa OPA handle
  * @retval None
  */
__weak void HAL_OPA_MspDeInit(OPA_HandleTypeDef *hopa)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hopa);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_OPA_MspDeInit could be implemented in the user file
   */

}

/**
  * @}
  */


/** @defgroup OPA_Exported_Functions_Group2 Input and Output operation functions
  *  @brief   Data transfers functions
  *
@verbatim
 ===============================================================================
                      ##### IO operation functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to manage the OPA data
    transfers.

@endverbatim
  * @{
  */

/**
  * @brief  Start the opa
  * @param  hopa OPA handle
  * @retval HAL status
  */

HAL_StatusTypeDef HAL_OPA_Start(OPA_HandleTypeDef *hopa)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPA handle allocation */
  /* Check if OPA locked */
  if (hopa == NULL)
  {
    status = HAL_ERROR;
  }
  else if (hopa->State == HAL_OPA_STATE_BUSYLOCKED)
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPA_ALL_INSTANCE(hopa->Instance));

    if (hopa->State == HAL_OPA_STATE_READY)
    {
      /* Enable the selected opa */
      SET_BIT(hopa->Instance->CSR, OPA_CSR_OPAEN);

      /* Update the OPA state*/
      /* From HAL_OPA_STATE_READY to HAL_OPA_STATE_BUSY */
      hopa->State = HAL_OPA_STATE_BUSY;
    }
    else
    {
      status = HAL_ERROR;
    }


  }
  return status;
}

/**
  * @brief  Stop the opa
  * @param  hopa OPA handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPA_Stop(OPA_HandleTypeDef *hopa)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPA handle allocation */
  /* Check if OPA locked */
  if (hopa == NULL)
  {
    status = HAL_ERROR;
  }
  else if (hopa->State == HAL_OPA_STATE_BUSYLOCKED)
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPA_ALL_INSTANCE(hopa->Instance));

    if (hopa->State == HAL_OPA_STATE_BUSY)
    {
      /* Disable the selected opa */
      CLEAR_BIT(hopa->Instance->CSR, OPA_CSR_OPAEN);

      /* Update the OPA state*/
      /* From  HAL_OPA_STATE_BUSY to HAL_OPA_STATE_READY*/
      hopa->State = HAL_OPA_STATE_READY;
    }
    else
    {
      status = HAL_ERROR;
    }
  }
  return status;
}



/**
  * @}
  */

/** @defgroup OPA_Exported_Functions_Group3 Peripheral Control functions
  *  @brief   Peripheral Control functions
  *
@verbatim
 ===============================================================================
                      ##### Peripheral Control functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to control the OPA data
    transfers.



@endverbatim
  * @{
  */

/**
  * @brief  Lock the selected opa configuration.
  * @param  hopa OPA handle
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_OPA_Lock(OPA_HandleTypeDef *hopa)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Check the OPA handle allocation */
  /* Check if OPA locked */
  /* OPA can be locked when enabled and running in normal mode */
  /*   It is meaningless otherwise */
  if (hopa == NULL)
  {
    status = HAL_ERROR;
  }
  else if (hopa->State != HAL_OPA_STATE_BUSY)
  {
    status = HAL_ERROR;
  }
  else
  {
    /* Check the parameter */
    assert_param(IS_OPA_ALL_INSTANCE(hopa->Instance));

    /* Lock OPA */
    SET_BIT(hopa->Instance->CSR, OPA_CSR_LOCK);

    /* OPA state changed to locked */
    hopa->State = HAL_OPA_STATE_BUSYLOCKED;
  }
  return status;
}

/**
  * @}
  */


/**
  * @brief  OPA IRQ handler.
  * @param  hopa  OPA handle
  * @retval None
  */
void HAL_OPA_IRQHandler(OPA_HandleTypeDef *hopa)
{
  uint32_t itsource = hopa->Instance->INTR;
  uint32_t itflag   = hopa->Instance->INTR;

  /* Capture compare 1 event */
  if (((itflag & OPA_INTR_INTIF) == OPA_INTR_INTIF) && ((itsource & OPA_INTR_INTEN) == OPA_INTR_INTEN))
  {  
    SET_BIT(hopa->Instance->INTR, OPA_INTR_CINTIF);
    /* COMP trigger user callback */
#if (USE_HAL_OPA_REGISTER_CALLBACKS == 1)
    hcomp->TriggerCallback(hopa);
#else
    HAL_OPA_TriggerCallback(hopa);
#endif /* USE_HAL_COMP_REGISTER_CALLBACKS */
  }
}

/**
  * @}
  */

/** @defgroup OPA_Exported_Functions_Group3 Peripheral Control functions
  *  @brief   Management functions.
  *
@verbatim
 ===============================================================================
                      ##### Peripheral Control functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to control the comparators.

@endverbatim
  * @{
  */


/**
  * @brief  Return the output level (high or low) of the selected comparator.
  *         The output level depends on the selected polarity.
  *         If the polarity is not inverted:
  *           - Comparator output is low when the input plus is at a lower
  *             voltage than the input minus
  *           - Comparator output is high when the input plus is at a higher
  *             voltage than the input minus
  *         If the polarity is inverted:
  *           - Comparator output is high when the input plus is at a lower
  *             voltage than the input minus
  *           - Comparator output is low when the input plus is at a higher
  *             voltage than the input minus
  * @param  hopa  OPA handle
  * @retval Returns the selected comparator output level:
  *         @arg OPA_OUTPUT_LEVEL_LOW
  *         @arg OPA_OUTPUT_LEVEL_HIGH
  *
  */
uint32_t HAL_OPA_GetOutputLevel(OPA_HandleTypeDef *hopa)
{

  return (uint32_t)(READ_BIT(hopa->Instance->CSR, OPA_CSR_OPA_CMP_OUT)
                    >> OPA_OUTPUT_LEVEL_BITOFFSET_POS);
}

/**
  * @brief  Comparator trigger callback.
  * @param  hopa  OPA handle
  * @retval None
  */
__weak void HAL_OPA_TriggerCallback(OPA_HandleTypeDef *hopa)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hopa);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_OAP_TriggerCallback should be implemented in the user file
   */
}


/**
  * @}
  */

/** @defgroup OPA_Exported_Functions_Group4 Peripheral State functions
  *  @brief   Peripheral State functions
  *
@verbatim
 ===============================================================================
                      ##### Peripheral State functions #####
 ===============================================================================
    [..]
    This subsection permit to get in run-time the status of the peripheral
    and the data flow.

@endverbatim
  * @{
  */

/**
  * @brief  Return the OPA state
  * @param  hopa OPA handle
  * @retval HAL state
  */
HAL_OPA_StateTypeDef HAL_OPA_GetState(OPA_HandleTypeDef *hopa)
{
  /* Check the OPA handle allocation */
  if (hopa == NULL)
  {
    return HAL_OPA_STATE_RESET;
  }

  /* Check the parameter */
  assert_param(IS_OPA_ALL_INSTANCE(hopa->Instance));

  return hopa->State;
}

/**
  * @}
  */

#if (USE_HAL_OPA_REGISTER_CALLBACKS == 1)
/**
  * @brief  Register a User OPA Callback
  *         To be used instead of the weak (surcharged) predefined callback
  * @param hopa : OPA handle
  * @param CallbackID : ID of the callback to be registered
  *        This parameter can be one of the following values:
  *          @arg @ref HAL_OPA_MSPINIT_CB_ID       OPA MspInit callback ID
  *          @arg @ref HAL_OPA_MSPDEINIT_CB_ID     OPA MspDeInit callback ID
  * @param pCallback : pointer to the Callback function
  * @retval status
  */
HAL_StatusTypeDef HAL_OPA_RegisterCallback(OPA_HandleTypeDef *hopa, HAL_OPA_CallbackIDTypeDef CallbackId,
                                             pOPA_CallbackTypeDef pCallback)
{
  HAL_StatusTypeDef status = HAL_OK;

  if (pCallback == NULL)
  {
    return HAL_ERROR;
  }

  /* Process locked */
  __HAL_LOCK(hopa);

  if (hopa->State == HAL_OPA_STATE_READY)
  {
    switch (CallbackId)
    {
      case HAL_OPA_MSPINIT_CB_ID :
        hopa->MspInitCallback = pCallback;
        break;
      case HAL_OPA_MSPDEINIT_CB_ID :
        hopa->MspDeInitCallback = pCallback;
        break;
      default :
        /* update return status */
        status =  HAL_ERROR;
        break;
    }
  }
  else if (hopa->State == HAL_OPA_STATE_RESET)
  {
    switch (CallbackId)
    {
      case HAL_OPA_MSPINIT_CB_ID :
        hopa->MspInitCallback = pCallback;
        break;
      case HAL_OPA_MSPDEINIT_CB_ID :
        hopa->MspDeInitCallback = pCallback;
        break;
      default :
        /* update return status */
        status =  HAL_ERROR;
        break;
    }
  }
  else
  {
    /* update return status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hopa);
  return status;
}

/**
  * @brief  Unregister a User OPA Callback
  *         OPA Callback is redirected to the weak (surcharged) predefined callback
  * @param hopa : OPA handle
  * @param CallbackID : ID of the callback to be unregistered
  *        This parameter can be one of the following values:
  *          @arg @ref HAL_OPA_MSPINIT_CB_ID              OPA MSP Init Callback ID
  *          @arg @ref HAL_OPA_MSPDEINIT_CB_ID            OPA MSP DeInit Callback ID
  *          @arg @ref HAL_OPA_ALL_CB_ID                   OPA All Callbacks
  * @retval status
  */

HAL_StatusTypeDef HAL_OPA_UnRegisterCallback(OPA_HandleTypeDef *hopa, HAL_OPA_CallbackIDTypeDef CallbackId)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Process locked */
  __HAL_LOCK(hopa);

  if (hopa->State == HAL_OPA_STATE_READY)
  {
    switch (CallbackId)
    {
      case HAL_OPA_MSPINIT_CB_ID :
        hopa->MspInitCallback = HAL_OPA_MspInit;
        break;
      case HAL_OPA_MSPDEINIT_CB_ID :
        hopa->MspDeInitCallback = HAL_OPA_MspDeInit;
        break;
      case HAL_OPA_ALL_CB_ID :
        hopa->MspInitCallback = HAL_OPA_MspInit;
        hopa->MspDeInitCallback = HAL_OPA_MspDeInit;
        break;
      default :
        /* update return status */
        status =  HAL_ERROR;
        break;
    }
  }
  else if (hopa->State == HAL_OPA_STATE_RESET)
  {
    switch (CallbackId)
    {
      case HAL_OPA_MSPINIT_CB_ID :
        hopa->MspInitCallback = HAL_OPA_MspInit;
        break;
      case HAL_OPA_MSPDEINIT_CB_ID :
        hopa->MspDeInitCallback = HAL_OPA_MspDeInit;
        break;
      default :
        /* update return status */
        status =  HAL_ERROR;
        break;
    }
  }
  else
  {
    /* update return status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hopa);
  return status;
}

#endif /* USE_HAL_OPA_REGISTER_CALLBACKS */

/**
  * @}
  */

#endif /* HAL_OPA_MODULE_ENABLED */
/**
  * @}
  */



